package com.xianniu.ltc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.xianniu.ltc.enums.ProjectReportEnum;
import com.xianniu.ltc.enums.ProjectReportTypeEnum;
import com.xianniu.ltc.model.entity.TProjectReportDTO;
import com.xianniu.ltc.model.po.ProjectReportPO;
import com.xianniu.ltc.service.ProjectReportService;
import com.xianniu.ltc.service.ProjectService;
import com.xxl.sso.core.entity.ReturnT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wzd
 * @Version 1.0
 * @Description :<项目报表统计>
 * @Date 2021/12/10 16:23
 **/
@Slf4j
@Service
public class ProjectReportServiceImpl implements ProjectReportService {

    @Value("#{'${REPORT-USER:}'.empty ? null : '${REPORT-USER:}'.split(',')}")
    private List<String> reportUser;
    @Autowired
    private ProjectService projectService;
    @Override
    public ReturnT getProjectReport(ProjectReportPO reportPO) {
        String mobileAddr = Application.getUserStore().initUserByJob(UserContextHolder.getUser(), UserContextHolder.getTenant()).getWorkphone();
        // 目前有权限查看的手机号
        ReturnT returnT = new ReturnT();
        log.info("当前访问项目报表的手机号{}:{}:{}",mobileAddr, UserContextHolder.getUser(), UserContextHolder.getTenant());
        if (reportUser == null){
            returnT = projectService.selectProjectReport(reportPO);
        }else {
            if (reportUser.contains(mobileAddr)){
                returnT = projectService.selectProjectReport(reportPO);
            }else {
                return returnT;
            }
        }
        if (ObjectUtils.isEmpty(returnT.getData())){
            return returnT;
        }
        List<TProjectReportDTO> listReport = (List<TProjectReportDTO>) returnT.getData();
                
        // 处理完数据的结果集
        List<TProjectReportDTO> reportVOS = processingData(listReport);

        // 将处理完数据的结果集按照type 类型进行分组
        Map<Integer, List<TProjectReportDTO>> reportMap = reportVOS.parallelStream().collect(Collectors.groupingBy(TProjectReportDTO::getReportType));

        //封装map
        Map<String,Object> reportMapList = new HashMap<>();
        // 获取回款统计计算
        List<TProjectReportDTO> payMentList = reportMap.get(ProjectReportTypeEnum.PAYMENT.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        //获取回款统计 计算结果
        List<JSONObject> mapListPayMent = paymentStatistics(payMentList);
        //处理饼形图
//        List<JSONObject> mapListPayMent = ringDiagram(bigDecimalMap);
        // Projectstatistics回款统计结果  项目回款统计BatchOperatorQuery.java:128
        reportMapList.put("projectStatistics",mapListPayMent);

        //获取到交付成本 已发生成本
        List<TProjectReportDTO> listCost = reportMap.get(ProjectReportTypeEnum.DELIVERY.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        //获取到交付成本 已发生成本 进行计算处理
        List<JSONObject> mapListCost = itemStatistics(listCost);
        //处理成饼图
//        List<JSONObject> mapListCost = ringDiagram(bigDecimalMap1);
        //交付成本统计
        reportMapList.put("deliveryCostStatistics",mapListCost);

        // 获取回款金额
        List<TProjectReportDTO> huiKuanList = reportMap.get(ProjectReportTypeEnum.HUIKUAN.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        // 处理成树状图
        JSONObject mapListHuiKuan = histogram(huiKuanList);
        //回款金额概况
        reportMapList.put("mapListHuiKuan",mapListHuiKuan);

        // 支出金额概况
        List<TProjectReportDTO> zhiChuList = reportMap.get(ProjectReportTypeEnum.ZHICHU.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        //处理成树状图
        JSONObject mapListZhiChu = histogram(zhiChuList);
        // 支出金额概况
        reportMapList.put("zhiChuList",mapListZhiChu);

        // 已支出费用明细
        List<TProjectReportDTO> expenditureList = reportMap.get(ProjectReportTypeEnum.EXPENDITUREDETAILS.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        expenditureList = merge(expenditureList);
        //处理数状图
        JSONObject mapListExpend = histogram(expenditureList);
        // 已支出费用明细
        reportMapList.put("yiZhiChuMap",mapListExpend);

        //服务单概况 树状图
        List<TProjectReportDTO> serviceList = reportMap.get(ProjectReportTypeEnum.SERVICESTATISTICS.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        // 服务单概况
        JSONObject mapListService = histogram(serviceList);

        // 计算服务单总计

        // 服务单结果 服务单统计
        reportMapList.put("mapListService",mapListService);

        //提货单 饼图
        List<TProjectReportDTO> billList = reportMap.get(ProjectReportTypeEnum.BILLOFLADING.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        // 计算提货单总计
        List<JSONObject> mapListBill = billStatistics(billList);
        // 封装提货单饼图
//        List<JSONObject> mapListBill = ringDiagram(billMap);
        // 提货单结果 提货单概况统计
        reportMapList.put("mapListBill",mapListBill);

        //交付工单概况统计 饼图
        List<TProjectReportDTO> deliverList = reportMap.get(ProjectReportTypeEnum.DELIVERYWORKORDER.getCode()).stream().sorted(Comparator.comparing(TProjectReportDTO::getDescribes)).collect(Collectors.toList());
        // 计算交付工单
        List<JSONObject> mapListWorkOrder = workStatistics(deliverList);
        // 处理结果为饼图
//        List<JSONObject> mapListWorkOrder = ringDiagram(workMap);
        // 交付工单概况统计
        reportMapList.put("mapListWorkOrder",mapListWorkOrder);

        log.info("输出结果集#{}", JSON.toJSON(reportMapList));
        return new ReturnT(reportMapList);
    }

    /**
     * 将id进行合并nums, sums 相加道回合并后的集合使用Java8的流进行处理
     */
    public static List<TProjectReportDTO> merge(List<TProjectReportDTO> list) {
        List<TProjectReportDTO> result = list.stream()
                // 表示id为key， 接着如果有重复的，那么从BillsNums对象o1与o2中筛选出一个，这里选择o1，
                // 并把id重复，需要将nums和sums与o1进行合并的o2, 赋值给o1，最后返回o1
                .collect(Collectors.toMap(TProjectReportDTO::getDescribes, a -> a, (o1,o2)-> {
                    o1.setNum(o1.getNum().add(o2.getNum()));
                    return o1;
                })).values().stream().collect(Collectors.toList());
        return result ;
    }

    /**
     * < 项目回款 >
     * @Params  参数
     * @return java.util.Map<java.lang.String,java.math.BigDecimal>
     * @author wzd
     * @date 2021/12/13 10:57
     * @version v1.0.0
     */
    private List<JSONObject> paymentStatistics( List<TProjectReportDTO> reportVOList){
        List<JSONObject> objectList = new ArrayList<>();
        JSONObject o1 = new JSONObject();
        JSONObject o2 = new JSONObject();
        // 已回款
        BigDecimal received =BigDecimal.ZERO;
        // 未回款
        BigDecimal unCollected = BigDecimal.ZERO;
        if (ObjectUtils.isNotEmpty(reportVOList)) {
            for (TProjectReportDTO e : reportVOList) {
                if (e.getDescribes().equals(ProjectReportEnum.A.getCode())) {
                    // 回款金额
                    received = received.add(e.getNum());
                } else {
                    // 未回款金额
                    unCollected = unCollected.add(e.getNum());
                }
            }
            // 项目回款统计
            BigDecimal sum = (received.add(unCollected)).compareTo(BigDecimal.ZERO)==0?BigDecimal.ONE:received.add(unCollected);
            BigDecimal a = received.divide(sum,2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
            // 回款金额
            o1.put("name", a+"%");
            o1.put("value", received);
            // 未回款金额
            if (unCollected.compareTo(BigDecimal.ZERO)==0){
                o2.put("name", "0%");
            }else {
                o2.put("name", new BigDecimal(100).subtract(a)+"%");
            }
            o2.put("value", unCollected);
        }else {
            o1.put("name", "0%");
            o1.put("value", BigDecimal.ZERO);
            // 已发生成本
            o2.put("name", "0%");
            o2.put("value", BigDecimal.ZERO);
        }
        objectList.add(o1);
        objectList.add(o2);
        return objectList;
    }


    /**
     * < 项目回款统计 >
     * @Params  参数
     * @return java.util.Map<java.lang.String,java.math.BigDecimal>
     * @author wzd
     * @date 2021/12/13 10:49
     * @version v1.0.0
     */
    private List<JSONObject> itemStatistics( List<TProjectReportDTO> projectReportVOS){
        List<JSONObject> objectList = new ArrayList<>();

        JSONObject o1 = new JSONObject();
        JSONObject o2 = new JSONObject();
        //发生成本金额
        BigDecimal  primeCost = BigDecimal.ZERO;
        // 交付成本上限金额
        BigDecimal totalAmount =BigDecimal.ZERO;
        //剩余可支出金额
        BigDecimal subtract = BigDecimal.ZERO;
        if (ObjectUtils.isNotEmpty(projectReportVOS)) {
            // 处理交付统计金额
            for (TProjectReportDTO e:projectReportVOS) {
                if (!e.getDescribes().equals(ProjectReportEnum.C.getCode())) {
                    // 计算已发生成本
                    primeCost = primeCost.add(e.getNum());
                } else {
                    //交付成本上限金额
                    totalAmount = totalAmount.add(e.getNum());
                }
            }
            //剩余可支出金额
            subtract = totalAmount.subtract(primeCost);
            if (subtract.compareTo(BigDecimal.ZERO) >0){
                BigDecimal a = subtract.divide(totalAmount,2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
                // 可剩余支出金额
                o1.put("name", a+"%");
                o1.put("value", subtract);
                // 已发生成本
                if (primeCost.compareTo(BigDecimal.ZERO)==0){
                    o2.put("name", "0%");
                }else {
                    o2.put("name", new BigDecimal(100).subtract(a)+"%");
                }
                o2.put("value", primeCost);
            }else {
                o1.put("name", "0%");
                o1.put("value", BigDecimal.ZERO);
                // 已发生成本
                o2.put("name", "0%");
                o2.put("value", BigDecimal.ZERO);
            }

        }else {
            o1.put("name", "0%");
            o1.put("value", BigDecimal.ZERO);
            // 已发生成本
            o2.put("name", "0%");
            o2.put("value", BigDecimal.ZERO);
        }
        objectList.add(o1);
        objectList.add(o2);
        return objectList;
    }


    /**
     * <提货单概况统计>
     * @Params  参数 projectReportVOS
     * @return java.util.Map<java.lang.String,java.math.BigDecimal>
     * @author wzd
     * @date 2021/12/13 15:25
     * @version v1.0.0
     */
    private List<JSONObject> billStatistics(List<TProjectReportDTO> projectReportVOS){
        List<JSONObject> objectList = new ArrayList<>();

        JSONObject o1 = new JSONObject();
        JSONObject o2 = new JSONObject();
        // 提货单备件数量
        BigDecimal  spareCount=BigDecimal.ZERO;
        // 备件DOA数量
        BigDecimal doaCount = BigDecimal.ZERO;
        if (ObjectUtils.isNotEmpty(projectReportVOS)) {
            for (TProjectReportDTO e:projectReportVOS) {
                if (e.getDescribes().equals(ProjectReportEnum.U.getCode())) {
                    spareCount = spareCount.add(e.getNum());
                } else {
                    doaCount = doaCount.add(e.getNum());
                }
            }
            // 提货单总数
            BigDecimal sum = (spareCount.add(doaCount)).compareTo(BigDecimal.ZERO)==0?BigDecimal.ONE:spareCount.add(doaCount);
            BigDecimal a = spareCount.divide(sum,2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
            // 提货单备件数量 spareCount
            o1.put("name", a+"%");
            o1.put("value", spareCount);
            // doa 备件
            if (doaCount.compareTo(BigDecimal.ZERO)==0){
                o2.put("name", "0%");
            }else {
                o2.put("name", new BigDecimal(100).subtract(a)+"%");
            }
            o2.put("value", doaCount);
        }else {
            // 提货单备件数量
            o1.put("name", "0%");
            o1.put("value", BigDecimal.ZERO);
            // doa 备件
            o2.put("name", "0%");
            o2.put("value", BigDecimal.ZERO);
        }
        objectList.add(o1);
        objectList.add(o2);
        return objectList;
    }


    /**
     * < 计算工单 >
     * @Params  参数 projectReportVOS
     * @return java.util.Map<java.lang.String,java.math.BigDecimal>
     * @author wzd
     * @date 2021/12/14 11:52
     * @version v1.0.0
     */
    private List<JSONObject> workStatistics(List<TProjectReportDTO> projectReportVOS){
        List<JSONObject> objectList = new ArrayList<>();

        JSONObject o1 = new JSONObject();
        JSONObject o2 = new JSONObject();
        // 正常工单
        BigDecimal normalOrder = BigDecimal.ZERO;
        // 异常工单 abnormal
        BigDecimal abnormal = BigDecimal.ZERO;
        if (ObjectUtils.isNotEmpty(projectReportVOS)) {
            for (TProjectReportDTO e:projectReportVOS) {
                if (e.getDescribes().equals(ProjectReportEnum.W.getCode())) {
                    normalOrder = normalOrder.add(e.getNum());
                } else {
                    abnormal = abnormal.add(e.getNum());
                }
            }
            BigDecimal sum = (normalOrder.add(abnormal)).compareTo(BigDecimal.ZERO)==0?BigDecimal.ONE:normalOrder.add(abnormal);
            BigDecimal a = normalOrder.divide(sum,2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
            // 正常工单 normalOrder
            o1.put("name", a+"%");
            o1.put("value", normalOrder);
            // 异常工单 abnormal
            if (abnormal.compareTo(BigDecimal.ZERO)==0){
                o2.put("name", "0%");
            }else {
                o2.put("name", new BigDecimal(100).subtract(a)+"%");
            }
            o2.put("value", abnormal);
        }else {
            // 提货单备件数量
            o1.put("name", "0%");
            o1.put("value", BigDecimal.ZERO);
            // doa 备件
            o2.put("name", "0%");
            o2.put("value", BigDecimal.ZERO);
        }
        objectList.add(o1);
        objectList.add(o2);
        return objectList;
    }




    /**
     * < 处理成饼形图格式 >
     * @Params  参数 map
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     * @author wzd
     * @date 2021/12/13 14:12
     * @version v1.0.0
     */

    private List<JSONObject> ringDiagram(Map<String,BigDecimal> map){
        if (ObjectUtils.isEmpty(map)){
            return new ArrayList<>();
        }
        List<JSONObject> mapList = new ArrayList<>();
        map.forEach((k,v)->{
            JSONObject o = new JSONObject();
            o.put("value",v);
            o.put("name",k);
            mapList.add(o);

        });
        return mapList;
    }


    /**
     * < 封装柱状图 >
     * @Params  参数 reportVOList
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     * @author wzd
     * @date 2021/12/13 14:40
     * @version v1.0.0
     */
    private JSONObject histogram(List<TProjectReportDTO> reportVOList){

        // 结果MapList
        JSONObject jsonObject = new JSONObject();
        // key List
        List<String> strList= new ArrayList<>();
        //value list
        List<BigDecimal> bigDecimalList = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(reportVOList)) {
            reportVOList.forEach(e -> {
                strList.add(ProjectReportEnum.getByCode(e.getDescribes()).getMsg());
                bigDecimalList.add(e.getNum());
            });

            jsonObject.put("name", strList);
            jsonObject.put("value", bigDecimalList);

        }else {
            jsonObject.put("name", strList);
            jsonObject.put("value", bigDecimalList);
        }
        return jsonObject;
    }

    /**
     * < 根据报表封装 为空的报表 >
     * @Params  参数 reportVOList 报表结果集
     参数 type  报表类型 某一报表
     * @return java.util.List<com.xianniu.api.model.app.project.vo.TProjectReportDTO>
     * @author wzd
     * @date 2021/12/14 14:27
     * @version v1.0.0
     */
    private List<TProjectReportDTO>  paramContainNull(){
        // 初始化一个 空的结果集对象
        List<TProjectReportDTO> projectReportVOList = new ArrayList<>();
        ProjectReportEnum[] values = ProjectReportEnum.values();
        for (ProjectReportEnum value : values) {
            TProjectReportDTO reportVO = new TProjectReportDTO();
            // 判断 枚举类型存储 类型

            if (value.getCode().equals(ProjectReportEnum.A.getCode()) || value.getCode().equals(ProjectReportEnum.B.getCode())){
                //项目回款
                reportVO.setReportType(ProjectReportTypeEnum.PAYMENT.getCode());
            }else if (value.getCode().equals(ProjectReportEnum.C.getCode()) || value.getCode().equals(ProjectReportEnum.D.getCode()) || value.getCode().equals(ProjectReportEnum.N.getCode())
                        || value.getCode().equals(ProjectReportEnum.O.getCode())){
                //交付成本
                reportVO.setReportType(ProjectReportTypeEnum.DELIVERY.getCode());

            }else if (value.getCode().equals(ProjectReportEnum.E.getCode()) || value.getCode().equals(ProjectReportEnum.F.getCode()) || value.getCode().equals(ProjectReportEnum.G.getCode())){
                // 回款金额
                reportVO.setReportType(ProjectReportTypeEnum.HUIKUAN.getCode());
            }else if (value.getCode().equals(ProjectReportEnum.H.getCode()) || value.getCode().equals(ProjectReportEnum.I.getCode())){
                //支出金额
                reportVO.setReportType(ProjectReportTypeEnum.ZHICHU.getCode());
            }else if (value.getCode().equals(ProjectReportEnum.J.getCode()) || value.getCode().equals(ProjectReportEnum.P.getCode()) || value.getCode().equals(ProjectReportEnum.Q.getCode())
                    || value.getCode().equals(ProjectReportEnum.K.getCode()) || value.getCode().equals(ProjectReportEnum.L.getCode()) || value.getCode().equals(ProjectReportEnum.M.getCode())) {
                // 支出费用明细规则
                reportVO.setReportType(ProjectReportTypeEnum.EXPENDITUREDETAILS.getCode());

            }else if (value.getCode().equals(ProjectReportEnum.R.getCode())|| value.getCode().equals(ProjectReportEnum.S.getCode()) || value.getCode().equals(ProjectReportEnum.T.getCode())){

                reportVO.setReportType(ProjectReportTypeEnum.SERVICESTATISTICS.getCode());


            }else if (value.getCode().equals(ProjectReportEnum.U.getCode()) || value.getCode().equals(ProjectReportEnum.V.getCode())){
                // 提货单概况统计
                reportVO.setReportType(ProjectReportTypeEnum.BILLOFLADING.getCode());

            }else if (value.getCode().equals(ProjectReportEnum.W.getCode()) ||value.getCode().equals(ProjectReportEnum.X.getCode())){
                // 派工单统计
                reportVO.setReportType(ProjectReportTypeEnum.DELIVERYWORKORDER.getCode());
            }


            reportVO.setDescribes(value.getCode());
            reportVO.setNum(BigDecimal.ZERO);
            projectReportVOList.add(reportVO);
        }
            return projectReportVOList;
    }



    /**
     * < 根据实际结果 对没有的结果集进行封装 >
     * @Params  参数 reportVOList
     * @return java.util.List<com.xianniu.api.model.app.project.vo.TProjectReportDTO>
     * @author wzd
     * @date 2021/12/14 15:13
     * @version v1.0.0
     */
    private  List<TProjectReportDTO> processingData(List<TProjectReportDTO> reportVOList){
        if (ObjectUtils.isEmpty(reportVOList)){
            return paramContainNull();
        }
        List<TProjectReportDTO> reportVOS = paramContainNull();
        //  按照所有的枚举类型进行分组
        Map<String, List<TProjectReportDTO>>  enumMapList= reportVOList.parallelStream().collect(Collectors.groupingBy(TProjectReportDTO::getDescribes));
        // 根据枚举去判断此对象存不存在，如果不存在将初始化的空对象 封装到实际结果集中
        reportVOS.forEach(e->{
            List<TProjectReportDTO> reportVOS1 = enumMapList.get(e.getDescribes());
            if (ObjectUtils.isEmpty(reportVOS1)){
                reportVOList.add(e);
            }
        });

        return reportVOList;
    }
}
